package org.hilo.boot.core.shiro;

import java.io.Serializable;
import java.util.Date;
import java.util.Map;

import org.apache.shiro.session.mgt.SimpleSession;
import org.hilo.boot.core.UT;

/**
 * 扩展了SimpleSession，在某些setAttribute和setLastAccessTime情况下，不会更新缓存。
 * see {@link CacheSessionDao}
 * 
 * @author zollty
 * @since 2017-07-02
 */
public class ExtSimpleSession extends SimpleSession implements Serializable {
    private static final long serialVersionUID = -3235621911060554350L;

    // 除lastAccessTime以外其他字段发生改变时为true
    private boolean changed;

    private long updateInterval;
    
    private long lastUpdateTime;

    public ExtSimpleSession() {
        super();
        this.setChanged(true);
    }

    public ExtSimpleSession(String host) {
        super(host);
        this.setChanged(true);
    }

    @Override
    public void setId(Serializable id) {
        super.setId(id);
        this.setChanged(true);
    }

    @Override
    public void setStopTimestamp(Date stopTimestamp) {
        super.setStopTimestamp(stopTimestamp);
        this.setChanged(true);
    }

    @Override
    public void setExpired(boolean expired) {
        super.setExpired(expired);
        this.setChanged(true);
    }

    @Override
    public void setTimeout(long timeout) {
        super.setTimeout(timeout);
        this.setChanged(true);
    }

    @Override
    public void setHost(String host) {
        super.setHost(host);
        this.setChanged(true);
    }

    @Override
    public void setAttributes(Map<Object, Object> attributes) {
        super.setAttributes(attributes);
        this.setChanged(true);
    }

    public boolean reachUpdateTime() {
        long last = getLastUpdateTime();
        long now = System.currentTimeMillis();
        // 如果N秒内访问 则不更新session，否则需要更新远端过期时间
        // 当然，这样会导致远端缓存里面的session过期时间，比实际过期时间少N秒，
        // 但是由于远端缓存只在内存缓存丢失后才会生效，所以只有服务器挂掉转移到另外的服务器上才会出现session少N秒的问题
        if ((now - last) >= getUpdateInterval()) {
            UT.getLogger().trace("(now - last) >= updateInterval, start to update cache...........");
            return true;
        }
        return false;
    }

    // 防止过于频繁的保存
    @Override
    public void setAttribute(Object key, Object value) {
        UT.getLogger().debug("save session Attribute {}", key);
        if(org.apache.shiro.web.util.WebUtils.SAVED_REQUEST_KEY.equals(key)) {
            super.setAttribute(key, value);
        } else {
            Object obj = this.getAttribute(key);
            if (obj != null && obj.equals(value)) {
                return;
            }
            super.setAttribute(key, value);
            this.setChanged(true); 
        }
    }

    @Override
    public Object removeAttribute(Object key) {
        this.setChanged(true);
        return super.removeAttribute(key);
    }

    /**
     * 停止
     */
    @Override
    public void stop() {
        super.stop();
        this.setChanged(true);
    }

    /**
     * 设置过期
     */
    @Override
    protected void expire() {
        this.stop();
        this.setExpired(true);
    }
    
    public boolean isChanged() {
        return changed;
    }

    public void setChanged(boolean changed) {
        this.changed = changed;
    }

    public long getLastUpdateTime() {
        return lastUpdateTime;
    }

    public void setLastUpdateTime(long lastUpdateTime) {
        this.lastUpdateTime = lastUpdateTime;
    }
    
    public long getUpdateInterval() {
        if (updateInterval < 3) {
            // 更新时间等于 timeout的十分之一，但不小于3秒
            this.updateInterval = getTimeout() / 10;
            this.updateInterval = updateInterval >= 3 ? updateInterval : 3;
            UT.getLogger().trace("Session setTimeout. Cache update Interval = {}", updateInterval);
        }
        return updateInterval;
    }
}
